package com.jzo2o.health.service.impl;

import com.jzo2o.api.trade.RefundRecordApi;
import com.jzo2o.api.trade.dto.response.ExecutionResultResDTO;
import com.jzo2o.common.constants.MqConstants;
import com.jzo2o.common.model.PageResult;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.model.CurrentUserInfo;

import com.jzo2o.common.utils.BeanUtils;

import com.jzo2o.common.utils.IdUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.health.constant.RabbitConstants;
import com.jzo2o.health.enums.OrderPayStatusEnum;
import com.jzo2o.health.enums.OrderStatusEnum;
import com.jzo2o.health.handler.OrdersHandler;
import com.jzo2o.health.mapper.OrdersMapper;
import com.jzo2o.health.model.UserThreadLocal;
import com.jzo2o.health.model.domain.*;
import com.jzo2o.health.model.dto.OrderCountDTO;
import com.jzo2o.health.model.dto.request.OrdersCancelReqDTO;
import com.jzo2o.health.model.dto.request.OrdersPageQueryReqDTO;
import com.jzo2o.health.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.health.model.dto.response.*;
import com.jzo2o.health.properties.TradeProperties;
import com.jzo2o.health.service.*;
import com.jzo2o.rabbitmq.client.RabbitClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;

import java.util.List;
import java.util.stream.Collectors;

/*
 *   Description IOrdersServiceImpl
 *   @author zhangyan
 *   @time   2024/12/14
 */
@Service
@Slf4j
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersService {
    @Autowired
    private ISetmealService setmealService;
    @Autowired
    private IMemberService memberService;
    @Autowired
    private IOrdersService owner;
    @Autowired
    private IReservationSettingService reservationSettingService;
    @Autowired
    private RabbitClient rabbitClient;


    /**
     * 下单
     *
     * @param placeOrderReqDTO
     * @return
     */
    @Override
    public PlaceOrderResDTO placeOrders(PlaceOrderReqDTO placeOrderReqDTO) {

        Orders orders = BeanUtils.copyBean(placeOrderReqDTO, Orders.class);

        //根据套餐Id查询套餐信息
        Setmeal setmeal = setmealService.findById(orders.getSetmealId());
        if (ObjectUtils.isEmpty(setmeal)) {
            throw new ForbiddenOperationException("套餐不存在");
        }

//        if (ObjectUtils.notEqual(orders.getCheckupPersonSex(), setmeal.getSex())){
//            throw new ForbiddenOperationException("当前套餐不支持当前性别");
//        }
        orders.setSetmealName(setmeal.getName());
        orders.setSetmealSex(setmeal.getSex());
        orders.setSetmealAge(setmeal.getAge());
        orders.setSetmealPrice(BigDecimal.valueOf(setmeal.getPrice()));
        orders.setSetmealImg(setmeal.getImg());
        orders.setSetmealRemark(setmeal.getRemark());
        //根据当前用户Id查询用户手机号
        Long userId = UserThreadLocal.currentUserId();
        Member member = memberService.getById(userId);
        if (ObjectUtils.isEmpty(member)) {
            throw new ForbiddenOperationException("用户不存在");
        }
        orders.setMemberId(member.getId());
        orders.setMemberPhone(member.getPhone());
        //封装订单状态信息以及id和sortby
        orders.setOrderStatus(OrderStatusEnum.NO_PAY.getStatus());
        orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());
        orders.setId(IdUtils.getSnowflakeNextId());
        orders.setSortBy(System.currentTimeMillis());

        boolean save = this.save(orders);
        if (!save) {
            throw new DBException("下单失败");
        }
        //预约人数+1
        reservationSettingService.addOne(orders.getReservationDate());

        //生成订单成功发送消息,超时订单处理
        rabbitClient.sendMsg(RabbitConstants.OVER_TIME_EXCHANG, RabbitConstants.OVER_TIME_ROUTING_KEY, orders.getId().toString(), (60000 * 2), // 2分钟
                IdUtils.getSnowflakeNextId(), false);

        return new PlaceOrderResDTO(orders.getId());
    }

    @Autowired
    private NativePayApi nativePayApi;
    @Autowired
    private TradeProperties tradeProperties;

    /**
     * 支付
     *
     * @param id
     * @param tradingChannel
     * @return
     */
    @Override
    public OrdersPayResDTO pay(Long id, PayChannelEnum tradingChannel) {

        //1判断订单是否存在
        Orders orders = this.getById(id);
        if (ObjectUtils.isEmpty(orders)) {
            throw new ForbiddenOperationException("订单不存在");
        }
        //2.判断订单是否处理过（已支付且有交易订单号）
        if (ObjectUtils.equal(orders.getPayStatus(), OrderPayStatusEnum.PAY_SUCCESS.getStatus()) && ObjectUtils.isNotEmpty(orders.getTradingOrderNo())) {
            throw new ForbiddenOperationException("订单已支付");
        }
        //3.调用支付服务的支付接口
        //3.1 根据支付渠道查询商户号
        Long enterpriseId = ObjectUtils.equal(tradingChannel, PayChannelEnum.WECHAT_PAY) ? tradeProperties.getWechatEnterpriseId() : tradeProperties.getAliEnterpriseId();
        //3.2 判断是否更换过支付渠道
        boolean isChange = false;
        if (ObjectUtils.isNotEmpty(orders.getTradingChannel()) && ObjectUtils.notEqual(orders.getTradingChannel(), tradingChannel.getValue())) {
            isChange = true;
        }
        //调用支付服务
        NativePayReqDTO nativePayReqDTO = new NativePayReqDTO();
        nativePayReqDTO.setEnterpriseId(enterpriseId);//商户号
        nativePayReqDTO.setProductAppId(tradeProperties.getProductAppId());//商户唯一标识AppId
        nativePayReqDTO.setProductOrderNo(orders.getId());
        nativePayReqDTO.setTradingChannel(tradingChannel);
        // nativePayReqDTO.setTradingAmount(orders.getSetmealPrice());
        nativePayReqDTO.setTradingAmount(BigDecimal.valueOf(0.01));
        nativePayReqDTO.setChangeChannel(isChange);//是否更换过支付渠道
        nativePayReqDTO.setMemo(orders.getSetmealName());//套餐名称
        NativePayResDTO nativePayResDTO = nativePayApi.createDownLineTrading(nativePayReqDTO);
        if (ObjectUtils.isEmpty(nativePayResDTO)) {
            throw new ForbiddenOperationException("调用支付服务失败");
        }
        //修改订单状态
        owner.updatePayOrders(id, nativePayResDTO);
        //封装响应结果
        OrdersPayResDTO ordersPayResDTO = BeanUtils.copyBean(nativePayResDTO, OrdersPayResDTO.class);
        ordersPayResDTO.setPayStatus(orders.getPayStatus());//这里还没有查询结果,应该返回未支付

        return ordersPayResDTO;
    }

    /**
     * 修改订单支付信息, 包交易订单号、交易渠道(支付状态和支付时间要调查询接口)
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updatePayOrders(Long id, NativePayResDTO nativePayResDTO) {
        boolean update = this.lambdaUpdate().eq(Orders::getId, id).set(Orders::getTradingChannel, nativePayResDTO.getTradingChannel()).set(Orders::getTradingOrderNo, nativePayResDTO.getTradingOrderNo()).update();
        if (!update) {
            throw new DBException("支付失败");
        }
    }

    @Autowired
    private TradingApi tradingApi;

    /**
     * 查询支付结果
     *
     * @param id
     * @return
     */
    @Override
    public OrdersPayResDTO payResult(Long id) {
        //1查询订单是否存在
        Orders orders = this.getById(id);
        if (ObjectUtils.isEmpty(orders)) {
            throw new ForbiddenOperationException("订单不存在");
        }
        //2判断支付单号是否存在
        if (ObjectUtils.isEmpty(orders.getTradingOrderNo())) {
            throw new ForbiddenOperationException("非法订单");
        }
        //3判断是否支付成功
        OrdersPayResDTO ordersPayResDTO = BeanUtils.copyBean(orders, OrdersPayResDTO.class);
        if (ObjectUtils.equal(orders.getPayStatus(), OrderPayStatusEnum.PAY_SUCCESS.getStatus())) {
            return ordersPayResDTO;
        }
        //4.调用支付服务Feign接口查询支付结果
        TradingResDTO result = tradingApi.findTradResultByTradingOrderNo(orders.getTradingOrderNo());
        if (ObjectUtils.isNotEmpty(result) && ObjectUtils.equal(result.getTradingState(), TradingStateEnum.YJS)) {
            //5.更新订单表信息（支付时间、第三方交易号、订单状态、支付状态）
            owner.paySuccess(id, result.getTransactionId(), result.getPaySuccessTime());
            //6.封装响应数据
            ordersPayResDTO.setQrCode(result.getQrCode());
            ordersPayResDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
        }
        log.info("查询支付结果，结果为：{}", result.getTradingState());
        return ordersPayResDTO;
    }

    /**
     * 支付成功更新订单
     *
     * @param orderId       订单ID
     * @param transactionId 第三方交易单ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void paySuccess(Long orderId, String transactionId, LocalDateTime payTime) {
        boolean result = this.lambdaUpdate().eq(Orders::getId, orderId).set(Orders::getPayTime, payTime).set(Orders::getTransactionId, transactionId).set(Orders::getOrderStatus, OrderStatusEnum.WAITING_CHECKUP.getStatus()).set(Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus()).update();
        if (!result) {
            throw new DBException("更新订单失败");
        }
    }

    @Autowired
    private IOrdersCancelService cancelService;

    /**
     * 取消未支付的订单
     *
     * @param dto
     */
    @Override
    public void cancelNoPay(OrdersCancelReqDTO dto, boolean isSystem) {
        //1判断订单是否存在
        Orders orders = this.getById(dto.getId());
        if (ObjectUtils.isNull(orders)) {
            log.error("订单不存在");
            return;
        }
        //2判断状态是否为待支付(0)
        if (ObjectUtils.notEqual(orders.getOrderStatus(), OrderStatusEnum.NO_PAY.getStatus()) && ObjectUtils.notEqual(orders.getOrderStatus(), OrderStatusEnum.WAITING_CHECKUP.getStatus())) {
            log.error("该状态的订单不能取消");
            return;
        }
        if (ObjectUtils.equal(orders.getOrderStatus(), OrderStatusEnum.CANCELLED.getStatus())) {
            return;
        }
        if (ObjectUtils.equal(orders.getOrderStatus(), OrderStatusEnum.WAITING_CHECKUP.getStatus())) {
            //todo 走退款逻辑
        }
        //取消订单, 更新订单状态,保存订单取消原因,并添加取消订单记录
        boolean update = this.lambdaUpdate().eq(Orders::getId, dto.getId()).set(Orders::getOrderStatus, OrderStatusEnum.CANCELLED.getStatus()).update();
        if (!update) {
            throw new DBException("取消订单失败");
        }
        OrdersCancelled ordersCancelled = new OrdersCancelled();
        ordersCancelled.setId(dto.getId());
        ordersCancelled.setCancelReason(dto.getCancelReason());
        if (isSystem) {
            CurrentUserInfo currentUserInfo = UserThreadLocal.currentUser();
            ordersCancelled.setCancellerId(currentUserInfo.getId());
            ordersCancelled.setCancellerName(currentUserInfo.getName());
            ordersCancelled.setCancellerType(currentUserInfo.getUserType());
        }
        ordersCancelled.setCancelTime(LocalDateTime.now());
        boolean save = cancelService.save(ordersCancelled);
        if (!save) {
            throw new DBException("添加取消订单记录失败");
        }
    }

    @Autowired
    private RefundRecordApi refundRecordApi;
    @Autowired
    private ApplicationContext applicationContext;

    /**
     * 取消订单退款
     *
     * @param dto
     */
    @Override
    public void cancelAndRefund(OrdersCancelReqDTO dto) {
        CurrentUserInfo currentUserInfo = UserThreadLocal.currentUser();
        //异步处理:取消订单,添加取消订单记录,退款
        OrdersHandler ordersHandler = applicationContext.getBean(OrdersHandler.class);
        ordersHandler.handleOrders(dto, currentUserInfo);

    }

    //==============================================================================================


    /**
     * 滚动分页查询
     *
     * @param ordersStatus
     * @param sortBy
     * @return
     */
    @Override
    public List<OrdersResDTO> pageQuery(Integer ordersStatus, Long sortBy) {
        List<Orders> orders = this.lambdaQuery().eq(Orders::getMemberId, UserThreadLocal.currentUserId()).eq(ObjectUtils.isNotEmpty(ordersStatus), Orders::getOrderStatus, ordersStatus).lt(ObjectUtils.isNotEmpty(sortBy), Orders::getSortBy, sortBy).last("limit 5").list();
        if (ObjectUtils.isEmpty(orders)) {
            return null;
        }
        List<OrdersResDTO> collect = orders.stream().map(x -> BeanUtils.copyBean(x, OrdersResDTO.class)).collect(Collectors.toList());

        return collect;
    }

    /**
     * 查询订单详情
     *
     * @param id
     * @return
     */
    @Override
    public OrdersDetailResDTO detail(Long id) {

        Orders orders = this.getById(id);
        if (ObjectUtils.isEmpty(orders)) {
            return null;
        }
        OrdersDetailResDTO ordersDetailResDTO = BeanUtils.copyBean(orders, OrdersDetailResDTO.class);
        if (ObjectUtils.equal(orders.getOrderStatus(), OrderStatusEnum.CANCELLED.getStatus()) || ObjectUtils.equal(orders.getOrderStatus(), OrderStatusEnum.CLOSED.getStatus())) {
            OrdersCancelled cancelled = cancelService.getById(id);
            ordersDetailResDTO.setCancelReason(cancelled.getCancelReason());
        }
        return ordersDetailResDTO;
    }

    /**
     * 后台查询订单列表
     *
     * @param dto
     * @return
     */
    @Override
    public PageResult<OrdersResDTO> pageQuery2Admin(OrdersPageQueryReqDTO dto) {

        Page<Orders> page = new Page<>(dto.getPageNo(), dto.getPageSize());
        Page<Orders> ordersPage = this.lambdaQuery().in(ObjectUtils.isNotEmpty(dto.getIds()), Orders::getId, dto.getIds()).eq(ObjectUtils.isNotEmpty(dto.getOrderStatus()), Orders::getOrderStatus, dto.getOrderStatus()).eq(ObjectUtils.isNotEmpty(dto.getMemberPhone()), Orders::getMemberPhone, dto.getMemberPhone()).orderByDesc(Orders::getSortBy).page(page);
        List<Orders> records = ordersPage.getRecords();
        List<OrdersResDTO> collect = records.stream().map(x -> BeanUtils.copyBean(x, OrdersResDTO.class)).collect(Collectors.toList());
        PageResult<OrdersResDTO> result = new PageResult<>();
        result.setList(collect);
        result.setPages(ordersPage.getPages());
        result.setTotal(ordersPage.getTotal());
        return result;
    }

    @Autowired
    private OrdersMapper ordersMapper;

    /**
     * 统计订单状态数量
     *
     * @return
     */
    @Override
    public OrdersCountResDTO countByStatus() {
        OrdersCountResDTO result = new OrdersCountResDTO();
        //查询订单各种状态数量
        List<OrderCountDTO> orderCountDTOS = ordersMapper.countByStatus();
        for (OrderCountDTO orderCountDTO : orderCountDTOS) {
            switch (orderCountDTO.getOrderStatus()) {
                case 0:
                    result.setNoPayCount(orderCountDTO.getCount());
                    break;
                case 100:
                    result.setWaitingCheckupCount(orderCountDTO.getCount());
                    break;
                case 200:
                    result.setCompletedCheckupCount(orderCountDTO.getCount());
                    break;
                case 300:
                    result.setClosedCount(orderCountDTO.getCount());
                    break;
                case 400:
                    result.setCancelledCount(orderCountDTO.getCount());
                    break;
            }
        }
        result.setTotalCount(result.getNoPayCount() + result.getWaitingCheckupCount() + result.getCompletedCheckupCount() + result.getClosedCount() + result.getCancelledCount());
        return result;
    }

    /**
     * 根据订单id聚合查询
     *
     * @param id
     * @return
     */
    @Override
    public AdminOrdersDetailResDTO aggregation(Long id) {
        AdminOrdersDetailResDTO result = new AdminOrdersDetailResDTO();
        AdminOrdersDetailResDTO.OrderInfo orderInfo = new AdminOrdersDetailResDTO.OrderInfo();
        AdminOrdersDetailResDTO.PayInfo payInfo = new AdminOrdersDetailResDTO.PayInfo();
        AdminOrdersDetailResDTO.CancelInfo cancelInfo = new AdminOrdersDetailResDTO.CancelInfo();
        AdminOrdersDetailResDTO.RefundInfo refundInfo = new AdminOrdersDetailResDTO.RefundInfo();
        Orders orders = this.getById(id);
        if (ObjectUtils.isEmpty(orders)) {
            throw new ForbiddenOperationException("订单不存在");
        }
        result.setOrderInfo(BeanUtils.copyBean(orders, AdminOrdersDetailResDTO.OrderInfo.class));
        if (ObjectUtils.equal(orders.getOrderStatus(), OrderStatusEnum.CANCELLED.getStatus())) {
            OrdersCancelled ordersCancelled = cancelService.getById(id);
            cancelInfo.setCancelReason(ordersCancelled.getCancelReason());
            cancelInfo.setCancelTime(ordersCancelled.getCancelTime());
        }
        if (ObjectUtils.equal(orders.getOrderStatus(), OrderStatusEnum.WAITING_CHECKUP.getStatus())) {
            payInfo.setPayStatus(orders.getPayStatus());
            payInfo.setThirdOrderId(orders.getTransactionId());
            payInfo.setTradingChannel(orders.getTradingChannel());
            payInfo.setPayTime(orders.getPayTime());
        }
        if (ObjectUtils.equal(orders.getOrderStatus(), OrderStatusEnum.CLOSED.getStatus())) {
            //如果关闭,设置退款信息
            refundInfo.setRefundId(orders.getRefundId());
            refundInfo.setRefundStatus(orders.getPayStatus());
            //设置取消理由
            OrdersCancelled ordersCancelled = cancelService.getById(id);
            refundInfo.setCancelReason(ordersCancelled.getCancelReason());

            refundInfo.setTradingChannel(orders.getTradingChannel());
            refundInfo.setCancelTime(ordersCancelled.getCancelTime());
        }

        if (ObjectUtils.equal(orders.getOrderStatus(), OrderStatusEnum.COMPLETED_CHECKUP.getStatus())) {
            //todo
        }
        //当订单表中支付状态为“未支付”时，这里支付状态为“未支付”；
        //当订单表中支付状态不是“未支付”时，这里支付状态为“已支付”；
        payInfo.setPayStatus(ObjectUtils.equal(orders.getPayStatus(), OrderPayStatusEnum.NO_PAY) ? OrderPayStatusEnum.NO_PAY.getStatus() : OrderPayStatusEnum.PAY_SUCCESS.getStatus());
        result.setPayInfo(payInfo);
        result.setRefundInfo(refundInfo);
        result.setCancelInfo(cancelInfo);
        return result;
    }
}
